import re
import zipfile
from copy import deepcopy
from io import BytesIO
from urllib.error import HTTPError
from urllib.request import urlopen
from uuid import uuid4
from wand.image import Image as _image
from xml.etree.ElementTree import parse

from lib.qiniucloud.qiniuMain import QiNiuService, QINIU_imgBucketDomain
from services.datamodel.question.models import mm_QUniversalTags


def get_id(e, ats, key):
    current_attr = ats.pop()
    for one in e:
        if not one.tag.endswith(f"{'}'}{current_attr}"):
            continue
        if ats:
            return get_id(one, ats, key)
        if not key:
            return one
        for k, v in one.attrib.items():
            if k.endswith(key):
                return f"{v}"


class Dispatch:
    def __new__(cls, ele):
        tag = Doc.parse_tag(ele.tag)[1]
        if tag in ["t", "instrText"]:
            init_cls = Text
        elif tag in ["drawing", "object"]:
            init_cls = Image
        elif tag == "tbl":
            init_cls = Table
        else:
            init_cls = cls
        return super(Dispatch, cls).__new__(init_cls)

    def __init__(self, ele):
        self.tag = Doc.parse_tag(ele.tag)[1]
        self.ele = ele
        self.style = ""

    def __repr__(self):
        return ""


class Text(Dispatch):
    def __init__(self, ele):
        super(Text, self).__init__(ele)

    def __repr__(self):
        return f"{self.ele.text}"


class Image(Dispatch):
    def __init__(self, ele):
        super(Image, self).__init__(ele)
        self.rid = ""

    def __repr__(self):
        if self.rid:
            pass

        elif self.tag == "drawing":
            ats = ["blip", "blipFill", "pic", "graphicData", "graphic",
                   "inline"]
            self.rid = get_id(self.ele, ats, "embed") or ""

        elif self.tag == "object":
            ats = ["imagedata", "shape"]
            self.rid = get_id(self.ele, ats, "id") or ""

        return self.rid


class Table(Dispatch):
    def __init__(self, ele):
        super(Table, self).__init__(ele)
        self.table = ""

    def __repr__(self):
        if self.table:
            return self.table

        trs = ""
        for tr in self.ele:
            if Doc.parse_tag(tr.tag)[1] != "tr":
                continue
            tcs = ""
            for tc in tr:
                if Doc.parse_tag(tc.tag)[1] != "tc":
                    continue
                col_span = get_id(tc, ["gridSpan", "tcPr"], "val")
                one = get_id(tc, ["t", "r", "p"], "")
                td = f"<td colspan='{col_span}'>" if col_span else "<td>"
                tcs += f"{td}{getattr(one, 'text', '')}</td>"
            trs += f"<tr>{tcs}</tr>"
        self.table = f"<table border='1'>{trs}</table>"
        return self.table


class Doc:
    def __init__(self, url_or_doc, subject_id=0):
        self._document = None
        self._paragraph = None
        self._rel = None
        self._rel_ship = None
        self._exam = None
        self._type = ""
        self._subject = subject_id
        self._q_tag_names = mm_QUniversalTags.dict_tree()
        self._qn = QiNiuService("img")
        self.__question = {"content": [], "answer": "", "explain": "",
                           "type": 1, "level": 1, "smalls": []}
        self.__exam = {"question": [], "name": "", "knowledge": ""}
        self._pre_type = ""

        for tag in mm_QUniversalTags.filter().values("id", "subjectid",
                                                     "name"):
            self._q_tag_names[tag["name"]][tag["subjectid"]] = tag["id"]

        try:
            if isinstance(url_or_doc, str) and url_or_doc.startswith(
                    "http") and "zgyjyx" in url_or_doc:
                resp = urlopen(url_or_doc)
                url_or_doc = BytesIO(resp.read())
            self._zipfile = zipfile.ZipFile(url_or_doc, "r")
        except (zipfile.BadZipFile, HTTPError, FileNotFoundError):
            assert False, (1, "not available doc or url")

    @staticmethod
    def parse_tag(tag):
        regex = re.compile(r"{(.*?)}(.*$)")
        match = regex.search(tag)
        if not match:
            return ["", ""]
        return match.groups()

    @property
    def label_tag(self):
        return ["r", "tbl"]

    @property
    def content_tag(self):
        return ["instrText", "t", "tr", "drawing", "object", "rPr"]

    @property
    def document(self):
        if self._document is None:
            raise KeyError("set document first")
        return self._document

    @document.setter
    def document(self, document):
        tags = self.parse_tag(document.tag)
        if tags[1] != "document":
            raise KeyError("root element is not document")
        self._document = document

    @property
    def body(self):
        for ele in self.document:
            if ele.tag.endswith("body"):
                return ele
        raise IndexError("document has error body")

    @property
    def rel(self):
        if self._rel is None:
            raise KeyError("set rel first")
        return self._rel

    @rel.setter
    def rel(self, rel):
        tags = self.parse_tag(rel.tag)
        if tags[1] != "Relationships":
            raise KeyError("root element is not Relationships")
        self._rel = rel

    @property
    def rel_ship(self):
        if self._rel_ship is None:
            self._rel_ship = {_.attrib["Id"]: _.attrib["Target"] for _ in
                              self.rel}
        return self._rel_ship

    @property
    def paragraph(self):
        if self._paragraph is None:
            self._paragraph = []
        return self._paragraph

    @property
    def exam(self):
        if self._exam is None:
            self._exam = []
        return self._exam

    def is_content_tag(self, tag):
        tags = self.parse_tag(tag)
        return tags[1] in self.content_tag

    def is_r_tag(self, tag):
        tags = self.parse_tag(tag)
        return tags[1] == "r"

    def is_table_tag(self, tag):
        tags = self.parse_tag(tag)
        return tags[1] == "tbl"

    def generate_r_ele(self, r):
        if self.is_table_tag(r.tag):
            return [r]

        cs = []
        if self.is_r_tag(r.tag):
            cs = [one for one in r if self.is_content_tag(one.tag)]
        return cs

    def _add_paragraph(self, p):
        if self.is_table_tag(p.tag):
            p = [p]

        if not p[:]:
            return

        p_style = ""
        if p[0].tag.endswith("pPr"):
            p_style = get_id(p[0], ["pStyle"], "val")
            p = p[1:]

        rs = []
        for r in p:
            r_ele = self.generate_r_ele(r)
            if not r_ele:
                continue

            for _r in r_ele:
                _d = Dispatch(_r)
                _d.style = get_id(_r, ["rStyle", "Pr"], "val") or p_style
                rs.append(_d)

        self.paragraph.append(rs)

    def _replace_target(self, e):
        target = self.rel_ship.get(e)
        if target:
            filename = f"word/{target}"
            png = f"{uuid4().hex}.png"
            steam = self._zipfile.read(filename)
            if filename.endswith("wmf"):
                with _image(blob=steam) as f:
                    steam = f.make_blob("png")
            self._qn.upload_file_by_steam(steam, png)
            e = f"<img src={QINIU_imgBucketDomain}{png} />"

        return e

    def _add_exam(self, p):
        content = ""
        for e in map(str, p):
            content += self._replace_target(e)
        content = content.replace('INCLUDEPICTURE  "', '<img src=')
        content = content.replace('INCLUDEPICTURE \d "', '<img src=')
        content = content.replace('" \* MERGEFORMATINET', ' >')

        # 试卷匹配
        if p and p[0].style in ["Char", "1Char", "2Char", "10", "11", "12",
                                "1", "2", "3", "4"]:
            self.exam.append(deepcopy(self.__exam))
            self.exam[-1]["name"] = content
            self._pre_type = ""
            return

        if not self.exam:
            return

        # 题型匹配
        check_type = content.strip()
        if len(check_type) < 20:
            if "知识点" in check_type:
                self._pre_type = "knowledge"
                return
            for name, info in self._q_tag_names.items():
                if name in check_type:
                    self._type = info.get(self._subject, 10)
                    return

        # 题目匹配
        match = re.search(r"^((\d+)[.．。]([123]?))?(\(\d+\)\.?)? ?(.*$)", content)
        g = match.groups()

        if g[2] == "":
            self.exam[-1]["question"].append(deepcopy(self.__question))
            self.exam[-1]["question"][-1]["type"] = self._type
            self.exam[-1]["question"][-1]["content"] = [g[-1]]
            self._pre_type = "content"
            return

        if self._pre_type == "knowledge":
            self.exam[-1]["knowledge"] += g[-1]
            return

        if not self.exam[-1]["question"]:
            return

        # 题目内容填充
        if g[3] is not None:
            self.exam[-1]["question"][-1]["smalls"].append([g[-1]])
            self._pre_type = "smalls"

        elif g[2] == "1":
            self.exam[-1]["question"][-1]["answer"] = g[-1]
            self._pre_type = "answer"

        elif g[2] == "2":
            self.exam[-1]["question"][-1]["explain"] = g[-1]
            self._pre_type = "explain"

        elif g[2] == "3":
            choice = {"简单": 1, "中等": 2, "困难": 3, "1": 1, "2": 2, "3": 3}
            self.exam[-1]["question"][-1]["level"] = choice.get(g[-1].strip(), 1)
            self._pre_type = "level"

        elif self._pre_type == "smalls":
            self.exam[-1]["question"][-1][self._pre_type][-1].append(g[-1])
        elif self._pre_type == "content":
            self.exam[-1]["question"][-1][self._pre_type].append(g[-1])
        elif self._pre_type in ("answer", "explain"):
            self.exam[-1]["question"][-1][self._pre_type] += g[-1]

    def main(self):
        with self._zipfile.open("word/document.xml", "r") as doc_f:
            doc_et = parse(doc_f)
            self.document = doc_et.getroot()

        with self._zipfile.open("word/_rels/document.xml.rels", "r") as rel_f:
            rel_et = parse(rel_f)
            self.rel = rel_et.getroot()

        for b in self.body:
            self._add_paragraph(b)

        for p in self.paragraph:
            if not p:
                continue
            self._add_exam(p)

        return self.exam


if __name__ == "__main__":
    d = Doc("/root/tmp/a.docx")
    d.main()
    print("book start")
    for i in d.exam:
        q = i.pop("question")
        print("exam start          ", i)
        for j in q:
            print("____________________one question               ", j)
            for k in j["smalls"]:
                print("--------------------one small             ", k)
